﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using MugenMvvmToolkit.ViewModels;
#if TASKSUPPORT
using System.Threading.Tasks;
#endif
using MugenMvvmToolkit.Interfaces;
using MugenMvvmToolkit.Models;

namespace MugenMvvmToolkit.Infrastructure
{
    /// <summary>
    ///     Represents a validator that use a <see cref="IValidationProvider" /> to validate objects.
    /// </summary>
    public class ValidationProviderValidator : ValidatorBase
    {
        #region Fields

        private IValidationProvider _validationProvider;

        #endregion

        #region Properties

        /// <summary>
        /// Indicates how should be call the Validate method, if <c>true</c> asynchronously; otherwise <c>false</c>.
        /// </summary>
        public bool IsAsyncValidate { get; set; }

        /// <summary>
        /// Indicates how should be call the ValidateAll method, if <c>true</c> asynchronously; otherwise <c>false</c>.
        /// </summary>
        public bool IsAsyncValidateAll { get; set; }

        #endregion

        #region Overrides of ValidatorBase

        /// <summary>
        /// Indicates that can be only once instance of this validator.
        /// </summary>
        public override bool IsUnique
        {
            get { return true; }
        }

        /// <summary>
        ///     Initializes the current validator using the specified <see cref="IValidatorContext" />.
        /// </summary>
        /// <param name="context">
        ///     The specified <see cref="IValidatorContext" />.
        /// </param>
        protected override void OnInitialized(IValidatorContext context)
        {
            Should.NotBeNull(context.ServiceProvider, "context.ServiceProvider");
            _validationProvider = context.ServiceProvider.GetService<IValidationProvider>();
        }

        /// <summary>
        ///     Checks to see whether the validator can validate objects of the specified IValidatorContext.
        /// </summary>
        protected override bool CanValidateInternal(IValidatorContext validatorContext)
        {
            return validatorContext.ServiceProvider != null;
        }

#if TASKSUPPORT
        /// <summary>
        ///     Updates information about errors in the specified property.
        /// </summary>
        /// <param name="propertyName">The specified property name.</param>        
        /// <returns>
        ///     The result of validation.
        /// </returns>
        protected override Task<IEnumerable> ValidateInternal(string propertyName)
        {
            var tcs = new TaskCompletionSource<IEnumerable>();
            var isAsyncValidate = IsAsyncValidate;
            Action action = () =>
                            {
                                try
                                {
                                    var listResults = new List<IValidationResult>();
                                    _validationProvider.ValidateProperty(InstanceToValidate, propertyName, listResults,
                                        Context.ValidationMetadata);
                                    tcs.SetResult(listResults);
                                }
                                catch (Exception exception)
                                {
                                    tcs.SetException(exception);
                                }
                            };
            if (isAsyncValidate)
            {
                var threadManager = (IThreadManager)Context.ServiceProvider.GetService(typeof(IThreadManager));
                threadManager.InvokeAsync(o => action());
            }
            else
                action();
            return tcs.Task;
        }

        /// <summary>
        ///     Updates information about all errors.
        /// </summary>
        /// <returns>
        ///     The result of validation.
        /// </returns>
        protected override Task<IDictionary<string, IEnumerable>> ValidateAllInternal()
        {
            var tcs = new TaskCompletionSource<IDictionary<string, IEnumerable>>();
            var isAsyncValidate = IsAsyncValidateAll;
            Action action = () =>
            {
                try
                {
                    var listResults = new List<IValidationResult>();
                    var resultDict = new Dictionary<string, IList<IValidationResult>>();
                    _validationProvider.ValidateObject(InstanceToValidate, false, listResults,
                                                       Context.ValidationMetadata);
                    _validationProvider.FillErrors(listResults, resultDict);
                    tcs.SetResult(resultDict.ToDictionary(pair => pair.Key, pair => (IEnumerable)pair.Value));
                }
                catch (Exception exception)
                {
                    tcs.SetException(exception);
                }
            };
            if (isAsyncValidate)
            {
                var threadManager = (IThreadManager)Context.ServiceProvider.GetService(typeof(IThreadManager));
                threadManager.InvokeAsync(o => action());
            }
            else
                action();
            return tcs.Task;
        }
#else
        /// <summary>
        ///     Updates information about errors in the specified property.
        /// </summary>
        /// <param name="propertyName">The specified property name.</param>
        protected override IOperationResult<IEnumerable> ValidateInternal(string propertyName)
        {
            var isAsyncValidate = IsAsyncValidate;
            Func<object, IEnumerable> action = (o) =>
            {
                var listResults = new List<IValidationResult>();
                _validationProvider.ValidateProperty(InstanceToValidate, propertyName, listResults,
                    Context.ValidationMetadata);
                return listResults;
            };

            IThreadManager threadManager = null;
            if (isAsyncValidate)
                threadManager = (IThreadManager)Context.ServiceProvider.GetService(typeof(IThreadManager));
            return OperationResult.FromDelegate(action,
                isAsyncValidate ? ExecutionType.Asynchronous : ExecutionType.None,
                threadManager);
        }

        /// <summary>
        ///     Updates information about all errors.
        /// </summary>        
        protected override IOperationResult<IDictionary<string, IEnumerable>> ValidateAllInternal()
        {
            var isAsyncValidate = IsAsyncValidateAll;
            Func<object, IDictionary<string, IEnumerable>> action = (o) =>
            {

                var listResults = new List<IValidationResult>();
                var resultDict = new Dictionary<string, IList<IValidationResult>>();
                _validationProvider.ValidateObject(InstanceToValidate, false, listResults,
                    Context.ValidationMetadata);
                _validationProvider.FillErrors(listResults, resultDict);
                return resultDict.ToDictionary(pair => pair.Key, pair => (IEnumerable)pair.Value);
            };
            IThreadManager threadManager = null;
            if (isAsyncValidate)
                threadManager = (IThreadManager)Context.ServiceProvider.GetService(typeof(IThreadManager));
            return OperationResult.FromDelegate(action,
                isAsyncValidate ? ExecutionType.Asynchronous : ExecutionType.None,
                threadManager);
        }
#endif
        /// <summary>
        ///     Creates a new validator that is a copy of the current instance.
        /// </summary>
        /// <returns>
        ///     A new validator that is a copy of this instance.
        /// </returns>
        protected override IValidator CloneInternal()
        {
            return new ValidationProviderValidator
                       {
                           IsAsyncValidate = IsAsyncValidate,
                           IsAsyncValidateAll = IsAsyncValidateAll
                       };
        }

        #endregion
    }
}